package utils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 工具类
 */
public class CommonUtils {

    /**
     * 输出map
     */
    public static void printMap(String msg, Map map){
        if(map != null && map.size() != 0){
            System.out.println(msg +  ":" + map);
        }else{
            System.out.println("|状态转换表不存在...");
        }
    }

    /**
     * move(T, a)
     * 从T中的某个状态s出发通过标记为a的边到达的状态的集合
     * @param set 转换前的集合
     * @param a 转换变量
     * @return 返回转换后的集合
     */
    public static Set<String> move(Set<String> set, String a, Map<String, Map<String, ArrayList<String>>> statusMap){
        if(set == null || set.size() == 0 || a.equals("") || a.equals("ε")){
            return null;
        }
        Set<String> retSet = new HashSet<>();
        for(String str : set){
            List<String> valueList = statusMap.get(str).get(a);
            retSet.addAll(valueList);
        }
        retSet.remove("^");
//        System.out.println("LexMain:move(): set=" + set + " change=" + a + " retSet=" + retSet);
        return retSet;
    }

    /**
     * closure(T)
     * 从T中的某个状态s出发通过ε到达的状态的集合
     * 其中 包含出发的状态
     * @param set 待判断的集合
     * @return 返回结果集
     */
    public static Set<String> closure(Set<String> set, Map<String, Map<String, ArrayList<String>>> statusMap){
//        System.out.println("LexMain:closure():set: " + set);
        if(set == null || set.size() == 0){
            return null;
        }
        Set<String> retSet = new HashSet<>();
        Set<String> hasChangeSet = new HashSet<>();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String str = iterator.next();
            hasChangeSet.add(str);  // 将当前判断的节点加入已经判断的节点集中
            retSet.add(str);
//            System.out.println("LexMain:closure():set:str " + str);
            List<String> valueList = statusMap.get(str).get("ε");
            retSet.addAll(valueList);   // 将通过ε新到达的加入结果集中
            set.addAll(valueList);  //将通过ε新到达的加入待判断的集合中
            set.removeAll(hasChangeSet);    //移除已判断的结点
            set.remove("^");
            // 每次迭代中由于对set进行了修改，所以需要重新获取迭代器，否则报错
            iterator = set.iterator();
        }
        retSet.remove("^");
        return retSet;
    }

    /**
     * 将确定化后的状态转换表持久化到文件中
     * @param chList 转换的条件
     * @param statusMap 状态转换表
     * @param startStatuses 开始状态集
     * @param endStatuses 结束状态集
     * @param filePath 存储的文件路径
     */
    public static void printNewToFile(String[] chList, Map<String, Map<String, String>> statusMap, List<String> startStatuses, List<String> endStatuses, String filePath){
        File statusChangeFile = new File(filePath);
        boolean isSuccessDeleteFile = statusChangeFile.delete();  //不管该文件是否存在，直接删除
        if(isSuccessDeleteFile || !statusChangeFile.exists()){ //如果删除成功，或者不存在该文件就新建一个
            try {
                System.out.println("|CommonUtils:printNewToFile: File not exist & Start create file...");
                boolean isSuccessCreateFile = statusChangeFile.createNewFile();
                if(!isSuccessCreateFile){
                    System.out.println("|CommonUtils:printNewToFile(): Filed to create file...");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        FileOutputStream fos = null;
        OutputStreamWriter osr = null;
        BufferedWriter bw = null;
        try {
            fos = new FileOutputStream(statusChangeFile);
            osr = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            bw = new BufferedWriter(osr);
            int changedStatusCount = 0;
            int usefulLen = ("ε".equals(chList[chList.length - 1])) ? (chList.length - 1) : chList.length;
            for(String change : chList){
                if(change.equals("ε")){
                    continue;
                }
                bw.write(change);
                changedStatusCount++;
                if(changedStatusCount < usefulLen){
                    bw.write("\t");
                }
            }
            // private Map<Set<String>, Map<String, Set<String>>> newStatusMap;
            Set<String> statusMapKeySet = statusMap.keySet();
            for(String key : statusMapKeySet){
                changedStatusCount = 0;
                bw.newLine();
                bw.write(key + "\t");
                for(String change : chList){
                    if(change.equals("ε")){
                        continue;
                    }
                    String value = statusMap.get(key).get(change);
                    bw.write(value);
                    changedStatusCount++;

                    if(changedStatusCount < usefulLen){
                        bw.write("\t");
                    }
                }
            }
            bw.newLine();
            bw.newLine();
            bw.write("startStatus : ");
            int statusCount = 0;
            for(String startStatus : startStatuses){
                bw.write(startStatus);
                statusCount++;
                if(statusCount < startStatuses.size()){
                    bw.write(",");
                }
            }
            bw.newLine();
            bw.write("endStatus : ");
            statusCount = 0;
            for(String endStatus : endStatuses){
                bw.write(endStatus);
                statusCount++;
                if(statusCount < endStatuses.size()){
                    bw.write(",");
                }
            }
            System.out.println("|CommonUtils:printNewToFile(): Success...");
            System.out.println("|CommonUtils:filePath: [" + filePath + "]");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(osr != null){
                try {
                    osr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将求得的LL(1)分析表持久化到文件中
     * @param allSelectableCharSet 可选字符
     * @param preParsingTable LL(1)分析表
     * @param filePath 存储的文件路径
     */
    public static void printLLPreParsingTableToFile(Set<String> allSelectableCharSet, Map<String, Map<String, String>> preParsingTable, String filePath){
        File statusChangeFile = new File(filePath);
        boolean isSuccessDeleteFile = statusChangeFile.delete();  //不管该文件是否存在，直接删除
        if(isSuccessDeleteFile || !statusChangeFile.exists()){ //如果删除成功，或者不存在该文件就新建一个
            try {
                System.out.println("|CommonUtils:printLLPreParsingTableToFile: File not exist & Start create file...");
                boolean isSuccessCreateFile = statusChangeFile.createNewFile();
                if(!isSuccessCreateFile){
                    System.out.println("|CommonUtils:printLLPreParsingTableToFile(): Filed to create file...");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        FileOutputStream fos = null;
        OutputStreamWriter osr = null;
        BufferedWriter bw = null;
        try {
            fos = new FileOutputStream(statusChangeFile);
            osr = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            bw = new BufferedWriter(osr);
            bw.write(String.format("%-8s", ""));
            for(String selectChar : allSelectableCharSet){
                bw.write(String.format("%-8s", selectChar));
            }
            Set<String> productionLeftSet = preParsingTable.keySet();
            for(String key : productionLeftSet){
                bw.newLine();
                bw.write(String.format("%-8s", key));
                for(String selectChar : allSelectableCharSet){
                    String value = preParsingTable.get(key).get(selectChar);
                    bw.write(String.format("%-8s", value));
                }
            }
            System.out.println("|CommonUtils:printNewToFile(): Success...");
            System.out.println("|CommonUtils:filePath: [" + filePath + "]");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(osr != null){
                try {
                    osr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
